"""
   ┌─┐       ┌─┐ + +
┌──┘ ┴───────┘ ┴──┐++
│                 │
│       ───       │++ + + +
███████───███████ │+
│                 │+
│       ─┴─       │
│                 │
└───┐         ┌───┘
    │         │
    │         │   + +
    │         │
    │         └──────────────┐
    │                        │
    │                        ├─┐
    │                        ┌─┘
    │                        │
    └─┐  ┐  ┌───────┬──┐  ┌──┘  + + + +
      │ ─┤ ─┤       │ ─┤ ─┤
      └──┴──┘       └──┴──┘  + + + +
             神兽保佑
            代码无BUG!

"""
import os

from lxml import html
from tqdm import tqdm
import pandas as pd
import requests
from fake_useragent import UserAgent


class Bilibili:
    def __init__(self):
        self.key_word = input("请输入关键字，多个关键字以中文逗号隔开：").split("，")
        self.stats_page = int(input("请输入开始的页数："))
        self.end_page = int(input("请输入结束的页数："))
        self.url = "https://api.bilibili.com/x/web-interface/search/type?context=&page={}&order=totalrank&keyword={}&duration=1&tids_2=&__refresh__=true&_extra=&search_type=video&tids=0&highlight=1&single_column=0"
        self.headers = {
            # 用户谷歌伪装
            "User-Agent": UserAgent().chrome,
            # 数据上一页面地址信息
            "referer": "https://search.bilibili.com/",

            "origin": "https://www.bilibili.com",
        }
        self.video_headers = {
            "User-Agent": UserAgent().chrome,
            "referer": "",
            "origin": "https://www.bilibili.com",
        }
        # 用于写入Excel文本
        self.content_DataFrame = pd.DataFrame(columns=["img_id", "content"])

    def get_cid(self, bvid):
        url = "https://api.bilibili.com/x/player/pagelist?bvid={}&jsonp=jsonp".format(bvid)
        json_ = requests.get(url, headers=self.headers).json()
        cid = json_["data"][0]["cid"]
        return cid

    def get_video_url(self, bvid, cid):
        url = 'https://api.bilibili.com/x/player/playurl?' + 'bvid=' + bvid + '&cid=' + str(
            cid) + '&qn=64&type=&otype=json'
        # 请求视屏真实地址时，需在referer中新添加原本视屏访问地址，否者会返回空数据
        self.video_headers["referer"] = 'http://www.bilibili.com/video/{}?from=search'.format(bvid)
        video_json = requests.get(url, headers=self.video_headers).json()
        video_url = video_json["data"]["durl"][0]["url"]
        return video_url

    def formatFloat(self, num):
        return '{:.2f}'.format(num)

    def save_video(self, video_url, key, video_name, MP4_id, page):
        try:
            video_data = requests.get(video_url, headers=self.video_headers, stream=True)
        except:
            print("跳过", video_url)
            return
        length = int(video_data.headers['content-length'])
        pbar = tqdm(total=length)
        with open("./" + key + "/" + page + "/" + MP4_id + "_" + video_name, 'wb') as f:
            for chunk in video_data.iter_content(1024 * 1024 * 2):
                f.write(chunk)
                pbar.set_description('正在下载中......' + MP4_id + "_" + video_name)
                pbar.update(1024 * 1024 * 2)  # 更新进度条长度
            pbar.close()

    def video_list(self, key):
        for page in range(self.stats_page, self.end_page):
            print("正在爬取", key, "第", page, "页")
            if not os.path.exists("./" + key):
                os.mkdir("./" + key)
            if not os.path.exists("./" + key + "/" + str(page)):
                os.mkdir("./" + key + "/" + str(page))
            json_ = requests.get(self.url.format(page, key)).json()
            mp4_id = 0
            if "result" not in json_["data"].keys():
                print(key, "已爬取完成")
                return
            for result in json_["data"]["result"]:
                mp4_id += 1
                bvid = result["bvid"]
                author = result["author"]
                title = result["title"]
                if title:
                    parse_txt = html.fromstring(title)
                    title = "".join(parse_txt.xpath("//text()"))
                else:
                    title = ""
                # 获取cid
                cid = self.get_cid(bvid)
                # 解析视屏真实地址
                try:
                    video_url = self.get_video_url(bvid, cid)
                except:
                    print("跳过")
                    continue
                # 视屏名称
                video_name = bvid + "_" + str(cid) + ".mp4"
                # 根据真实地址下载视屏
                self.save_video(video_url, key, video_name, str(mp4_id), str(page))
                print(author + ":", result["arcurl"], "下载成功", page)
                self.content_DataFrame = self.content_DataFrame.append(
                    pd.DataFrame({"img_id": [str(mp4_id)], "content": [title]}))
            self.content_DataFrame.to_excel("./" + key + "/" + str(page) + "/视屏标题.xlsx", '视屏标题', index=False)
            self.content_DataFrame.drop(self.content_DataFrame.index, inplace=True)

    def run(self):
        self.user_input()
        for key in self.key_word:
            self.video_list(key)

    def user_input(self):
        print(
            """
   ┌─┐       ┌─┐ + +
┌──┘ ┴───────┘ ┴──┐++
│                 │
│       ───       │++ + + +
███████───███████ │+
│                 │+
│       ─┴─       │
│                 │
└───┐         ┌───┘
    │         │
    │         │   + +
    │         │
    │         └──────────────┐
    │                        │
    │                        ├─┐
    │                        ┌─┘
    │                        │
    └─┐  ┐  ┌───────┬──┐  ┌──┘  + + + +
      │ ─┤ ─┤       │ ─┤ ─┤
      └──┴──┘       └──┴──┘  + + + +
             神兽保佑
            代码无BUG!
            
            可乐：开始奔跑
"""
        )
        print("输入关键字", len(self.key_word), "个")


if __name__ == '__main__':
    bi = Bilibili()
    bi.run()
